using Android.App;
using Android.Views;
using Android.Widget;
using CustomListViewAdapter.Models;
using System;
using System.Collections.Generic;
using System.Linq;

namespace CustomListViewAdapter.Adapters
{
    public class ListViewAdapter<T> : BaseAdapter<T>, IFilterable
    {
        protected List<T> Items;
        protected List<T> OriginalItems;
        private ListViewFilter<T> _listViewfilter;
        private int _resource;

        private readonly Func<T, View, ViewGroup, View> _viewFunc;
        private readonly Func<T, View, View> _simpleViewFunc;
        protected Func<List<T>, List<T>> PerformFiltering;

        public ListViewAdapter(List<T> items, Func<T, View, ViewGroup, View> getView, Func<List<T>, List<T>> performFiltering = null)
        {
            this.Items = items;
            this._viewFunc = getView;
            this.PerformFiltering = performFiltering;
        }

        public ListViewAdapter(List<T> items, int resource, Func<T, View, View> simpleViewFunc, Func<List<T>, List<T>> performFiltering = null)
        {
            this.Items = items;
            this._resource = resource;
            this._simpleViewFunc = simpleViewFunc;
            this.PerformFiltering = performFiltering;
        }

        #region BaseAdapter

        public override T this[int position]
        {
            get { return Items[position]; }
        }

        public override int Count
        {
            get { return Items.Count; }
        }

        public override long GetItemId(int position)
        {
            var item = Items[position] as Entity;
            return null != item ? item.Id : 0;
        }

        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            if (null != _simpleViewFunc)
                return _simpleViewFunc(this.Items[position], convertView ?? LayoutInflater.FromContext(Application.Context).Inflate(_resource, parent, false));
            else
                return _viewFunc(this.Items[position], convertView, parent);
        }

        #endregion BaseAdapter

        #region Methods

        public void Add(T item)
        {
            if (null != OriginalItems)
                this.OriginalItems.Add(item);
            else
                this.Items.Add(item);

            this.NotifyDataSetChanged();
        }

        public void Remove(T item)
        {
            if (null != OriginalItems)
                this.OriginalItems.Remove(item);
            else
                this.Items.Remove(item);

            this.NotifyDataSetChanged();
        }

        public void AddRange(IEnumerable<T> items)
        {
            if (null != OriginalItems)
                this.OriginalItems.AddRange(items);
            else
                this.Items.AddRange(items);

            this.NotifyDataSetChanged();
        }

        #endregion Methods

        #region IFilterable

        public Filter Filter
        {
            get
            {
                if (null == this._listViewfilter)
                    this._listViewfilter = new ListViewFilter<T>(this);

                return this._listViewfilter;
            }
        }

        #endregion IFilterable

        private class ListViewFilter<T> : Filter
        {
            private readonly ListViewAdapter<T> _listViewAdapter;

            public ListViewFilter(ListViewAdapter<T> listViewAdapter)
            {
                this._listViewAdapter = listViewAdapter;
            }

            protected override Filter.FilterResults PerformFiltering(Java.Lang.ICharSequence constraint)
            {
                FilterResults results = new FilterResults();

                if (null == _listViewAdapter.OriginalItems)
                    _listViewAdapter.OriginalItems = new List<T>(_listViewAdapter.Items);

                var filteringResults = _listViewAdapter.PerformFiltering(_listViewAdapter.OriginalItems);
                results.Values = new JavaObject<List<T>>(filteringResults);
                results.Count = filteringResults.Count;

                return results;
            }

            protected override void PublishResults(Java.Lang.ICharSequence constraint, Filter.FilterResults results)
            {
                this._listViewAdapter.Items = ((JavaObject<List<T>>)results.Values).Value;

                if (results.Count == 0)
                    _listViewAdapter.NotifyDataSetInvalidated();
                else
                    _listViewAdapter.NotifyDataSetChanged();
            }
        }

        private class JavaObject<T> : Java.Lang.Object
        {
            public JavaObject(T obj)
            {
                this.Value = obj;
            }

            public T Value { get; private set; }
        }
    }
}